import React, { useState, useEffect, forwardRef, useRef } from 'react';
import { Graph, Cell, Node, Edge } from '@antv/x6';
import Lane from './components/Lane';
import List from './components/List';
import CreateNode from './components/CreateNode';
import ActionButtons from './components/ActionButtons';
import { Spin } from 'antd';
import {
  useMemoizedFn,
  useInterval,
  useDebounceEffect,
  useMount,
  useUpdateEffect,
} from 'ahooks';
import useHooks from './useHooks';
import {
  getGraphData,
  nextTick,
  createGraphNodeData,
  dbHelper,
  autoSaveJson,
  getKey,
  getGraphJSONData,
  defaultModuleNodeName,
  defaultEdgeShapeName,
} from './tools';
import defaultData from './defaultData';
import iconfont from './components/assets/font/iconfont';
import {
  getNodeAttr,
  registerAll,
  registerEdge,
  registerModuleNode,
} from './components/Lane/tools';
import {
  ActionTypes,
  ArchData,
  GraphData,
  ModuleConfig,
  NodeData,
  Register,
} from './types';

import './style/index';

(window as any)._globalAssets = {};

export interface ArchDesignProps {
  assets?: { [key: string]: string };
  ref?: any;
  data: ArchData;
  moduleConfig: ModuleConfig;
  autoSaveInterval?: number; // 数据自动保存的间隔时间 默认为0,不自动保存
  showActions?: boolean;
  actions?: ActionTypes[];
  appendActions?: React.ReactNode;
  registerList?: Register[];
  cache?: boolean; // 是否要使用缓存
  defaultEdgeShape?: string;
  defaultNodeShape: string;
  moduleNodeShape?: string;
  drawerRootNode?: false | string | HTMLElement | (() => HTMLElement);

  onAddLink?: (link: Edge) => Promise<boolean>;
  onRemoveLink?: (links: Edge[]) => Promise<boolean>;
  onRemoveNode?: (nodes: Node[]) => Promise<boolean>;
  onCreateNode?: (nodeData: NodeData) => Promise<NodeData>;
  onUpdateNode?: (nodeData: NodeData) => Promise<NodeData>;
  onUndoOrRedo?: (
    type: 'undo' | 'redo',
    graph: Graph,
    args: { cmds: any[]; options: any },
  ) => void;
  onAutoSave?: (data: ArchData) => any;
  onSave?: (data: ArchData) => Promise<boolean>;
  onChange?: (data: ArchData) => any;
}
const ArchDesign: React.FC<ArchDesignProps> = forwardRef(function (
  {
    assets,
    moduleConfig,
    data,
    onAddLink,
    onRemoveLink,
    onRemoveNode,
    onCreateNode,
    onUpdateNode,
    onUndoOrRedo,
    onAutoSave,
    onSave,
    onChange,
    autoSaveInterval = 0,
    showActions,
    actions,
    appendActions,
    registerList,
    defaultNodeShape,
    defaultEdgeShape = defaultEdgeShapeName,
    moduleNodeShape = defaultModuleNodeName,
    drawerRootNode,
    cache,
  },
  api: any,
) {
  (window as any)._saveInterval = autoSaveInterval;
  (window as any)._useCache = cache;
  const archWrapper = useRef(null);
  const [createModalKey, setCreateModalKey] = useState<string>(getKey());
  const [laneKey, setLaneKey] = useState<string>(getKey());
  const [graph, setGraph] = useState<Graph>();
  const [visible, setVisible] = useState<boolean>(false);
  const [initLoading, setInitLoading] = useState<boolean>(true);
  const [canRedo, setCanRedo] = useState<boolean>(false);
  const [canUndo, setCanUndo] = useState<boolean>(false);
  const [currentCell, setCurrentCell] = useState<Cell>();
  const [graphData, setGraphData] = useState<GraphData>(defaultData);
  const [assetsData, setAssetsData] = useState((window as any)._globalAssets);
  const [zoom, setZoom] = useState<number>(1);

  // 节点或线发生变化时调用
  const changeCells = useMemoizedFn(() => {
    if (graph && onChange) {
      onChange(getGraphJSONData(graph));
    }
  });
  if (api && Reflect.has(api, 'current')) {
    api.current = {
      resize: () => {
        restoreData(getGraphJSONData(graph as Graph), false);
        setLaneKey(getKey());
      },
      getArchData: () => getGraphJSONData(graph as Graph),
    };
  }
  // 全屏显示
  const [isFullScreen, setIsFullScreen] = useState<boolean>(false);
  // 初始化数据
  // 从 indexdb 中恢复数据
  const restoreData = useMemoizedFn(async (data: ArchData, cache?: boolean) => {
    setInitLoading(true);
    let nodeDatas: ArchData = data;
    if (cache) {
      await dbHelper.ready();
      const store: any = await dbHelper.findByPk('graphDatas', '1');
      const { id: storeId, updateTime, expired, data: storeData } = store || {};
      if (storeId && updateTime + expired > Date.now()) {
        if (storeData) nodeDatas = storeData as ArchData;
      }
    }
    setGraphData(getGraphData(nodeDatas, defaultEdgeShape));
    setInitLoading(false);
    await autoSaveJson(null, graph, onAutoSave);
  });
  // 自动保存
  const autoSaveStop = useInterval(
    async () => {
      await autoSaveJson(null, graph, onAutoSave);
    },
    autoSaveInterval,
    { immediate: !!autoSaveInterval },
  );

  useEffect(() => {
    if (!autoSaveInterval) {
      autoSaveStop();
    }
  }, [autoSaveInterval]);

  // 合并资源
  useEffect(() => {
    const globalAssets = { ...assetsData, ...(assets || {}) };
    (window as any)._globalAssets = globalAssets;
    setAssetsData(globalAssets);
  }, [assets]);

  useDebounceEffect(() => {
    restoreData(data, cache);
  }, [data, cache]);

  // 生成事件
  const { actionFunMap } = useHooks({
    onRemoveLink,
    onRemoveNode,
    onAddLink,
    onSave,
    changeCells,
    defaultEdgeShape,
  });

  // 画布事件响应
  const handlerActions = useMemoizedFn((action: string) => {
    actionFunMap[action] &&
      actionFunMap[action](currentCell, graph, {
        setVisible,
        setCurrentCell,
        setZoom,
        zoom,
        fullContainer: archWrapper.current,
        setIsFullScreen,
      });
  });

  // 拖拽进入创建环节
  const dragToCreateNode = (node: Node) => {
    setCurrentCell(node);
    nextTick(() => setVisible(true));
  };

  // 创建新节点
  const addNewNode = useMemoizedFn((nodeData: any) => {
    const lane = graph?.getCellById(nodeData.preId);
    const node = graph?.createNode(
      createGraphNodeData(nodeData, { graph, assets: assetsData }),
    );
    if (node && lane) {
      lane.addChild(node);
    }
    if (currentCell) {
      graph?.removeCell(currentCell);
      setCurrentCell(undefined);
    }
    changeCells();
  });
  // 修改节点信息
  const updateNode = useMemoizedFn((node: Node) => {
    const nodeData: any = node.data || {};
    const { shape } = node;
    const grahpNode = graph?.getCellById(nodeData.id);
    grahpNode?.setData({ ...nodeData }, { silent: true });
    grahpNode?.attr(getNodeAttr(shape, nodeData, assets));
    changeCells();
  });

  // 创建或修改节点信息
  const submitNode = useMemoizedFn(async (node: Node) => {
    let flag: boolean = false;
    let nodeData: any = node.data || {};
    const isNew: boolean = !nodeData.id;
    let ret: any = undefined;
    if (isNew) {
      if (onCreateNode) {
        ret = await onCreateNode(nodeData);
      } else {
        flag = true;
      }
    } else {
      if (onUpdateNode) {
        ret = await onUpdateNode(nodeData);
      } else {
        flag = true;
      }
    }
    if (!flag && !!ret) {
      nodeData = ret;
      flag = true;
    }
    // 如果flag true 创建节点
    if (flag) {
      if (isNew) {
        addNewNode(nodeData);
      } else {
        updateNode(node);
      }
    }
    return flag;
  });

  useMount(() => {
    registerAll(registerList);
    registerModuleNode();
    registerEdge();
    iconfont();
  });

  useUpdateEffect(() => {
    if (visible) setCreateModalKey(getKey());
  }, [visible]);

  return (
    <div ref={archWrapper} className="ra-arch-design">
      <Spin spinning={initLoading} tip="数据初始化中，请稍候...">
        <div className="ra-arch-aside">
          <List
            onNodeCreate={dragToCreateNode}
            configs={moduleConfig}
            graph={graph}
            assets={assetsData}
            defaultNodeShape={defaultNodeShape}
            moduleNodeShape={moduleNodeShape}
          />
        </div>
        <div className="ra-arch-content">
          <CreateNode
            key={createModalKey}
            visible={visible}
            onClose={() => setVisible(false)}
            node={currentCell as Node}
            onSubmit={submitNode}
            rootNode={drawerRootNode}
          />
          <div className="ra-arch-actions">
            <ActionButtons
              onActions={handlerActions}
              isFullScreen={isFullScreen}
              canRedo={canRedo}
              canUndo={canUndo}
              showActions={showActions}
              actions={actions}
              appendActions={appendActions}
            />
          </div>
          <div className="ra-arch-laneGroup">
            <Lane
              key={laneKey}
              setCurrentCell={setCurrentCell}
              data={graphData}
              onActions={handlerActions}
              onLoad={setGraph}
              assets={assetsData}
              zoom={zoom}
              isFullScreen={isFullScreen}
              setCanRedo={setCanRedo}
              setCanUndo={setCanUndo}
              onUndoOrRedo={onUndoOrRedo}
              defaultEdgeShape={defaultEdgeShape}
            />
          </div>
        </div>
      </Spin>
    </div>
  );
});

export default ArchDesign;
